/*
 * FileTAR.cpp
 *
 * Implementacia zdroja adresara: TAR
 *
 *  Created on: 1.4.2011
 *      Author: Petr Hodac, Tomas Hlavnicka
 */

#include "FileTAR.h"

#define cache_size cend-coff
#define remaining len-boff

FileTAR::FileTAR(std::string source) {
	this->source = source;

	FILE* f = fopen(source.c_str(), "r");
	if(f == NULL) throw "TAR file does not exist.";
	fclose(f);

	tar = NULL;
	coff = 0;
	cend = 0;
	_eof = false;
}

void FileTAR::open(const char * filename, const char * mode) {
	if(NULL != tar)
		this->close();

	int oflags = 0;

	if(strcmp(mode,"r") == 0)
		oflags=O_RDONLY;
	else if (strcmp(mode, "w") == 0)
		oflags=O_WRONLY;
	else
		throw "Invalid mode.";

	if(tar_open(&tar, const_cast<char*>(source.c_str()),
			NULL, oflags, 0 /* tarmode */, TAR_GNU) != 0){
		throw "Can't open TAR file.";
	}

	bool found = false;
	while(0 == th_read(tar))
	{
		char * pathname = th_get_pathname(tar);
		if(0 == strcmp(pathname, filename))
		{
			found = true;
			fsize = th_get_size(tar);
		}
		free(pathname);
		if(found) break;
		tar_skip_regfile(tar);
	}
	if(strcmp(mode, "r") == 0 && !found)
		throw "File not found in TAR";
	else if(strcmp(mode, "w") == 0)
		throw "TAR is read-only";

	_eof = false;
	coff = cend = 0;
}

int FileTAR::read(void * buf, int len)  {
	if(NULL == tar) throw "No tar is opened.";
	if(eof()) throw "EOF";
	int boff = 0;

	/*
	 * cache_size >= len:
	 * 		vratime do dlzky len a posunieme offset cache
	 */
	if(cache_size >= len)
	{
		memcpy((char*)buf+boff, cache+coff, len);
		coff += len;
		boff += len;
		return boff;
	}
	/*
	 * cache_size < len:
	 * 		posleme celu cache, ale nacitame dalsie data
	 * 		posunieme buffer offset a cache vyprazdnime
	 */
	else
	{
		memcpy((char*)buf+boff, cache+coff, cache_size);
		boff += cache_size;
		coff = cend = 0;
	}

	// len-boff = kolko nam este ostava poslat

	/*
	 * Kym nam ostava viac nez T_BLOCKSIZE, posielame
	 * to rovno do vystupneho bufferu
	 */
	while(remaining >= T_BLOCKSIZE && fsize >= T_BLOCKSIZE)
	{
		tar_block_read(tar, (char*)buf+boff);
		boff += T_BLOCKSIZE;
		fsize -= T_BLOCKSIZE;
	}
	/*
	 * Zostatok read do cache a pozadovanu cast poslat
	 */
	tar_block_read(tar, cache);
	if(fsize < T_BLOCKSIZE)
	{
		cend = fsize;
		fsize = 0;
		_eof = true;
	}
	else
	{
		cend = T_BLOCKSIZE;
		fsize -= T_BLOCKSIZE;
	}

	/*
	 * Pokial nam nieco ostalo v cache, poslime to
	 */
	if(remaining > 0 && cache_size > 0)
	{
		int s = (remaining < cache_size ? remaining : cache_size);
		memcpy((char*)buf+boff, cache+coff, s);
		boff += s;
		coff += s;
	}

	return boff;
}

int FileTAR::write(const void * buf, int len)  {
	throw "TAR is read-only";
	return 0;
}

bool FileTAR::eof() {
	return _eof && (cache_size == 0);
}

void FileTAR::close() {
	if(NULL == tar) throw "No file is opened.";
	tar_close(tar);
	tar = NULL;
}

std::string FileTAR::type() const {
	return std::string("TAR");
}

FileTAR::~FileTAR() {
	if(NULL == tar) return;
	close();
}
